昨天介紹了有關gymnasium的一些基礎概念,而今天我們會一步一步來建立我們的Frozen Lake。這篇文章寫得有點亂,自己一時之間也沒有想到更好的改法,請大家見諒。
首先我將這整個定義成一個類別FrozenLakeAgent
,並初始化一些參數,在未來會使用到,而其中一些參數在第8天的時候有提到過,如果有人忘記了,可以回去複習一下喔。
我們的類別內主要會有以下幾個功能:
def __init__(self, map_name="8x8", is_slippery=True, alpha=0.9, gamma=0.9, epsilon=1, epsilon_decay_rate=0.0001):
self.map_name = map_name
self.is_slippery = is_slippery
self.alpha = alpha
self.gamma = gamma
self.epsilon = epsilon
self.epsilon_decay_rate = epsilon_decay_rate
self.rng = np.random.default_rng()
這裡面有很多參數在Day8介紹Q-Learning的時候有提到,如果有忘記的部分,可以回去看看文章。
接著來到我們主要的訓練部分,在這裡我們定義了一個 train 函數,這個函數的主要功能是讓代理在多次遊玩 Frozen Lake 的過程中,不斷更新 Q table,從中學習如何獲得最大的回報。
def train(self, episodes, render=False):
if render:
self.env = gym.make('FrozenLake-v1', map_name=self.map_name, is_slippery=self.is_slippery, render_mode='human')
else:
self.env = gym.make('FrozenLake-v1', map_name=self.map_name, is_slippery=self.is_slippery)
self.q_table = np.zeros((self.env.observation_space.n, self.env.action_space.n))
# 創建一個零值的 Q-table,大小為 (狀態數, 行動數)。Q-table 用於儲存每個狀態下的每個行動的 Q 值(即價值),並在訓練過程中進行更新。每個格子對應於一個狀態-行動組合的預期獎勵。
# self.env.observation_space.n:表示環境中所有可能狀態的數量。
# self.env.action_space.n:表示環境中可用的行動數量(如上、下、左、右四個方向)。
self.env.observation_space.n:表示環境中所有可能狀態的數量。
self.env.action_space.n:表示環境中可用的行動數量(如上、下、左、右四個方向)。
rewards_per_episode = np.zeros(episodes)
在 train 函數中,我們首先初始化 Q 表格,其大小是根據環境的狀態空間與行動空間決定的,然後開始進行多次遊玩,這裡我們會傳入 episodes 參數來決定遊玩的次數。
接著在每次遊戲中,代理需要根據 epsilon-greedy 策略選擇行動,並不斷更新 Q 表格以學習環境。
而什麼是epsilon-greedy策略呢?我們留在明天繼續介紹!今天先把程式碼的部分介紹完。(文章的結構編排真的好難,想了很久還是沒想到要怎麼介紹比較好)
for i in range(episodes):
state = self.env.reset()[0]
terminated = False
truncated = False
while not terminated and not truncated:
if self.rng.random() < self.epsilon:
action = self.env.action_space.sample() # 探索
else:
action = np.argmax(self.q_table[state, :]) # 利用
new_state, reward, terminated, truncated, _ = self.env.step(action)
self.q_table[state, action] += self.alpha * (reward + self.gamma * np.max(self.q_table[new_state, :]) - self.q_table[state, action])
state = new_state
terminated = False
和 truncated = False
if self.rng.random() < self.epsilon:
這裡運用了 Epsilon-Greedy 策略 來決定代理是進行探索(exploration)還是利用(exploitation):
探索:action = self.env.action_space.sample()
利用:action = np.argmax(self.q_table[state, :])
new_state, reward, terminated, truncated, _ = self.env.step(action)
最後state = new_state
今天先將初始化及訓練的部分先完成及介紹,我們可以注意到整個Q-learning的核心訓練流程,並且介紹了如何透過Epsilon greedy strategy策略來平衡探索與利用。這是強化學習中的重要概念,能夠幫助代理逐步學會如何在環境中做出最佳決策。今天的篇幅有點長,所以明天會接續將未完成的部分繼續完成。